package com.inesv.api.network;

import com.inesv.api.baseclasses.BaseRpcSend;
import com.inesv.api.utils.JSONUtil;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.*;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;

/**
 * creaete by liukeling
 * class name DefaultRPCSend
 * 对rpc请求的默认实现方式
 */
public class DefaultRPCSend extends BaseRpcSend {
    /**
     * 构造方法  初始节点基本参数
     *
     * @param nodeRPCIP       钱包服务器的ip
     * @param nodeRPCPort     钱包服务器的端口
     * @param nodeRPCUser     钱包rpc用户名
     * @param nodeRPCPassword 钱包rpc密码
     */
    public DefaultRPCSend(String nodeRPCIP, String nodeRPCPort, String nodeRPCUser, String nodeRPCPassword) {
        super(nodeRPCIP, nodeRPCPort, nodeRPCUser, nodeRPCPassword);
    }
    Charset QUERY_CHARSET = Charset.forName("ISO8859-1");

    /**
     * rpc准备发送请求
     * @param method    rpc方法名
     * @param params    rpc方法所需要的参数
     * @return    返回解析好的byte数组
     */
    public byte[] prepareRequest(final String method, final Object[] params) {
        return JSONUtil.stringify(new LinkedHashMap() {
            {
                put("jsonrpc", "1.0");
                put("id", "1");
                put("method", method);
                put("params", params);
            }
        }).getBytes(QUERY_CHARSET);
    }
    /**
     * 根据请求内容请求rpc接口返回所需的结果
     * @param requestBody  rpc请求内容
     * @param uri          rpc请求url
     * @return             返回rpc请求结果  请求失败返回空字符串
     */
    public String generateRequest(String requestBody, String uri) {
        authenticator();
        HttpURLConnection connection = getRPCConnection(uri,requestBody);
        if(connection != null){
            return toConnection(connection);
        }
        return "";
    }
    /**
     * 获取该节点的rpcurl
     * @return  节点的rpcurl地址
     */
    @Override
    public String getNodeUri() {
        return "http://"+nodeRPCIP+":"+nodeRPCPort;
    }

    /**
     * rpc认证
     */
    protected void authenticator(){
        final PasswordAuthentication temp = new PasswordAuthentication(
                this.nodeRPCUser, this.nodeRPCPassword.toCharArray());
        Authenticator.setDefault(new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return temp;
            }
        });
    }

    /**
     * 根据url和请求内容获取连接对象
     * @param uri
     * @param requestBody
     * @return
     */
    protected HttpURLConnection getRPCConnection(String uri, String requestBody){
        HttpURLConnection connection = null;
        try {
            URL url = new URL(uri);
            connection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
            connection.setDoOutput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("Content-Length",
                    Integer.toString(requestBody.getBytes().length));
            connection.setUseCaches(true);
            connection.setDoInput(true);
            connection.setConnectTimeout(6000);
            OutputStream out = connection.getOutputStream();
            out.write(requestBody.getBytes());
            out.flush();
            out.close();
        } catch (Exception ioE) {
            if(connection != null)connection.disconnect();
            return null;
        }
        return connection;
    }

    /**
     * 根据连接对象发送连接请求并获取请求内容
     * @param connection
     * @return
     */
    protected String toConnection(HttpURLConnection connection){
        try {
            int httpCode = connection.getResponseCode();

            if (httpCode == HttpURLConnection.HTTP_OK || httpCode == 500) {
                InputStream is = httpCode == 500?connection.getErrorStream():connection.getInputStream();
                BufferedReader rd = new BufferedReader(
                        new InputStreamReader(is));
                String line;
                StringBuffer response = new StringBuffer();
                while ((line = rd.readLine()) != null) {
                    response.append(line);
                    response.append('\r');
                }
                rd.close();
                String responseToString = response.toString();
                return responseToString;
            } else {
                connection.disconnect();
            }
        } catch (Exception e) {
        }
        return "";
    }
}
